﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.ComponentModel;
using Microsoft.Win32;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Reflection;
using System.Diagnostics;
using System.Xml;
using System.IO;
using System.Collections.ObjectModel;

using AvalonDock;
using ICSharpCode.AvalonEdit;
using ICSharpCode.AvalonEdit.Highlighting;
using ICSharpCode.AvalonEdit.Highlighting.Xshd;
using ICSharpCode.AvalonEdit.Folding;

namespace delge_squirreleditor
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public static int MaxFilenameLength = 15;

        DockableContent projectExplorerContent;

        TreeView projectExplorer;

        ScriptEditor activeEditor;
        Project activeProject;

        Process scriptProcess;

        public static RoutedCommand CommandNewProject = new RoutedCommand();        // Ctrl + Left Shift + N
        public static RoutedCommand CommandNewFile = new RoutedCommand();           // Ctrl + N
        public static RoutedCommand CommandOpenProject = new RoutedCommand();       // Ctrl + Left Shift + O
        public static RoutedCommand CommandOpenFile = new RoutedCommand();          // Ctrl + O
        public static RoutedCommand CommandCloseProject = new RoutedCommand();      // Ctrl + Left Shift + X
        public static RoutedCommand CommandCloseFile = new RoutedCommand();         // Ctrl + X
        public static RoutedCommand CommandSaveFile = new RoutedCommand();          // Ctrl + S
        public static RoutedCommand CommandSaveAll = new RoutedCommand();           // Ctrl + Left Alt + S
        public static RoutedCommand CommandSaveProject = new RoutedCommand();       // Ctrl + Left Shift + S
        public static RoutedCommand CommandRun = new RoutedCommand();               // F5

        public string StatusText
        {
            get { return statusBarItem_Status.Content as string; }
            set
            {
                statusBarItem_Status.Content = value as string;
            }
        }

        public MainWindow()
        {
            InitializeComponent();

            projectExplorer = new TreeView();
            projectExplorer.Name = "projectExplorer";

            projectExplorerContent = new DockableContent() { Title = "Project Explorer", Content = projectExplorer };
            projectExplorerContent.Show(dockManager, AnchorStyle.Left);

            ThemeFactory.ChangeTheme(new Uri("/AvalonDock.Themes;component/themes/dev2010.xaml", UriKind.RelativeOrAbsolute));

            menuItem_Show_ProjectExplorer.Click += new RoutedEventHandler(menuItem_Show_ProjectExplorer_Click);

            menuItem_File_SaveAs.Click += new RoutedEventHandler(menuItem_File_SaveAs_Click);
            menuItem_File_Exit.Click += new RoutedEventHandler(menuItem_File_Exit_Click);

            menuItem_Project_CreateDistributable.Click += new RoutedEventHandler(menuItem_Project_CreateDistributable_Click);
            menuItem_Project_OpenProjectFolder.Click += new RoutedEventHandler(menuItem_Project_OpenProjectFolder_Click);

            menuItem_Help_Squirrel_LanguageReference.Click += new RoutedEventHandler(menuItem_Help_Squirrel_LanguageReference_Click);
            menuItem_Help_Squirrel_StandardLibraryReference.Click += new RoutedEventHandler(menuItem_Help_Squirrel_StandardLibraryReference_Click);
            menuItem_Help_DelGEReference.Click += new RoutedEventHandler(menuItem_Help_DelGEReference_Click);
            menuItem_Help_About.Click += new RoutedEventHandler(menuItem_Help_About_Click);

            projectExplorer.MouseDoubleClick += new MouseButtonEventHandler(projectExplorer_MouseDoubleClick);
            projectExplorer.KeyDown +=  new KeyEventHandler(projectExplorer_KeyDown);

            dockManager.ActiveDocumentChanged += new EventHandler(dockManager_ActiveDocumentChanged);

            InputBindings.Add(new InputBinding(CommandNewProject, new KeyGesture(Key.N, ModifierKeys.Control | ModifierKeys.Shift)));
            InputBindings.Add(new InputBinding(CommandNewFile, new KeyGesture(Key.N, ModifierKeys.Control)));
            InputBindings.Add(new InputBinding(CommandOpenProject, new KeyGesture(Key.O, ModifierKeys.Control | ModifierKeys.Shift)));
            InputBindings.Add(new InputBinding(CommandOpenFile, new KeyGesture(Key.O, ModifierKeys.Control)));
            InputBindings.Add(new InputBinding(CommandCloseProject, new KeyGesture(Key.X, ModifierKeys.Control | ModifierKeys.Shift)));
            InputBindings.Add(new InputBinding(CommandCloseFile, new KeyGesture(Key.X, ModifierKeys.Control)));
            InputBindings.Add(new InputBinding(CommandSaveFile, new KeyGesture(Key.S, ModifierKeys.Control)));
            InputBindings.Add(new InputBinding(CommandSaveAll, new KeyGesture(Key.S, ModifierKeys.Control | ModifierKeys.Alt)));
            InputBindings.Add(new InputBinding(CommandSaveProject, new KeyGesture(Key.S, ModifierKeys.Control | ModifierKeys.Shift)));
            InputBindings.Add(new InputBinding(CommandRun, new KeyGesture(Key.F5)));

            CommandBindings.Add(new CommandBinding(CommandNewProject, CommandNewProjectExecute, CommandNewProjectCanExecute));
            CommandBindings.Add(new CommandBinding(CommandNewFile, CommandNewFileExecute, CommandNewFileCanExecute));
            CommandBindings.Add(new CommandBinding(CommandOpenProject, CommandOpenProjectExecute, CommandOpenProjectCanExecute));
            CommandBindings.Add(new CommandBinding(CommandOpenFile, CommandOpenFileExecute, CommandOpenFileCanExecute));
            CommandBindings.Add(new CommandBinding(CommandCloseProject, CommandCloseProjectExecute, CommandCloseProjectCanExecute));
            CommandBindings.Add(new CommandBinding(CommandCloseFile, CommandCloseFileExecute, CommandCloseFileCanExecute));
            CommandBindings.Add(new CommandBinding(CommandSaveFile, CommandSaveFileExecute, CommandSaveFileCanExecute));
            CommandBindings.Add(new CommandBinding(CommandSaveAll, CommandSaveAllExecute, CommandSaveAllCanExecute));
            CommandBindings.Add(new CommandBinding(CommandSaveProject, CommandSaveProjectExecute, CommandSaveProjectCanExecute));
            CommandBindings.Add(new CommandBinding(CommandRun, CommandRunExecute, CommandRunCanExecute));

            menuItem_File_New_Project.Command = CommandNewProject;
            menuItem_File_New_File.Command = CommandNewFile;
            menuItem_File_Open_Project.Command = CommandOpenProject;
            menuItem_File_Open_File.Command = CommandOpenFile;
            menuItem_File_CloseProject.Command = CommandCloseProject;
            menuItem_File_Close.Command = CommandCloseFile;
            menuItem_File_Save.Command = CommandSaveFile;
            menuItem_File_SaveAll.Command = CommandSaveAll;
            menuItem_File_SaveProject.Command = CommandSaveProject;
            menuItem_Project_Run.Command = CommandRun;

            UpdateMenus();
        }

        private void ToggleMenus_Project(bool toggle, string filename = "")
        {
            menuItem_Project.IsEnabled = toggle;
            menuItem_File_New_File.IsEnabled = toggle;
            menuItem_File_CloseProject.IsEnabled = toggle;
            menuItem_File_SaveProject.IsEnabled = toggle;

            if (filename != "")
                filename = "\"" + filename + "\"";

            menuItem_File_CloseProject.Header = "Close Project " + filename;
            menuItem_File_SaveProject.Header = "Save Project " + filename;
        }

        private void ToggleMenus_Editor(bool toggle, string filename = "")
        {
            menuItem_File_Close.IsEnabled = toggle;
            menuItem_File_Save.IsEnabled = toggle;
            menuItem_File_SaveAs.IsEnabled = toggle;

            if (filename != "")
                filename = "\"" + filename + "\"";

            menuItem_File_Close.Header = "Close " + filename;
            menuItem_File_Save.Header = "Save " + filename;
            menuItem_File_SaveAs.Header = "Save " + filename + " As...";
        }

        private void UpdateMenus()
        {
            ToggleMenus_Project(false);
            ToggleMenus_Editor(false);

            if (activeEditor != null)
                ToggleMenus_Editor(true, activeEditor.Shortname);

            if (activeProject != null)
                ToggleMenus_Project(true, activeProject.Shortname);
        }

        protected override void OnClosing(CancelEventArgs e)
        {
            if (activeProject != null)
                activeProject.Save();
            if (!ScriptEditor.CloseAll())
                e.Cancel = true;
            if (!e.Cancel)
            {
                if (scriptProcess != null && scriptProcess.HasExited == false)
                    scriptProcess.CloseMainWindow();
            }
            base.OnClosing(e);
        }

        void projectExplorer_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (projectExplorer.SelectedItem is ProjectItem)
            {
                string filepath = (projectExplorer.SelectedItem as ProjectItem).FilePath;
                // Only do this if the file exists
                if (File.Exists(filepath))
                {
                    (projectExplorer.SelectedItem as ProjectItem).OpenEditor(dockManager);
                    activeEditor = (projectExplorer.SelectedItem as ProjectItem).Editor;
                }

                UpdateMenus();
            }
        }

        void projectExplorer_KeyDown(object sender, KeyEventArgs e)
        {
            if (projectExplorer.SelectedItem is ProjectItem)
            {
                if (e.Key == Key.F2)
                    (projectExplorer.SelectedItem as ProjectItem).BeginEdit();
            }
        }

        void dockManager_ActiveDocumentChanged(object sender, EventArgs e)
        {
            activeEditor = null;

            // Only set an active editor if there is one to actually be set,
            // avalondock seems to call this event twice and messes things up,
            // so by keeping check we can tell what should really happen \o/
            if (ScriptEditor.ScriptEditorList.Count > 0)
                activeEditor = dockManager.ActiveDocument as ScriptEditor;

            UpdateMenus();
        }

        void menuItem_File_Exit_Click(object sender, RoutedEventArgs e)
        {
            this.Close();
        }

        void menuItem_Project_CreateDistributable_Click(object sender, RoutedEventArgs e)
        {
            if (activeProject != null)
            {
                string [] binaryFiles = {"delge_squirrel.exe", "fmodex.dll", "FreeImage.dll", "msvcp100.dll", "msvcr100.dll"};
                string pathToInterpreter = Path.GetDirectoryName(GetType().Assembly.Location) + "\\bin";
                string binaryPath = Path.GetDirectoryName(activeProject.Filepath) + "\\bin";
                string scriptsPath = binaryPath + Project.ScriptSubPath;
                string screensPath = binaryPath + Project.ScreenSubPath;
                string classesPath = binaryPath + Project.ClasseSubPath;

                if (!Directory.Exists(binaryPath)) Directory.CreateDirectory(binaryPath);
                if (!Directory.Exists(scriptsPath)) Directory.CreateDirectory(scriptsPath);
                if (!Directory.Exists(screensPath)) Directory.CreateDirectory(screensPath);
                if (!Directory.Exists(classesPath)) Directory.CreateDirectory(classesPath);

                foreach (string file in binaryFiles)
                {
                    if (file == "delge_squirrel.exe")
                        File.Copy(pathToInterpreter + "\\" + file, binaryPath + "\\" + activeProject.Name + ".exe", true);
                    else
                        File.Copy(pathToInterpreter + "\\" + file, binaryPath + "\\" + file, true);

                }

                foreach (ProjectItem item in activeProject.Scripts)
                    File.Copy(item.FilePath, scriptsPath + "\\" + item.FileName, true);
                foreach (ProjectItem item in activeProject.Screens)
                    File.Copy(item.FilePath, screensPath + "\\" + item.FileName, true);
                foreach (ProjectItem item in activeProject.Classes)
                    File.Copy(item.FilePath, classesPath + "\\" + item.FileName, true);

                Process.Start(binaryPath);

                StatusText = "Distribution Folder Created";
            }
        }

        void menuItem_Project_OpenProjectFolder_Click(object sender, RoutedEventArgs e)
        {
            if (activeProject != null)
                Process.Start(activeProject.Path);
        }

        void menuItem_Show_ProjectExplorer_Click(object sender, RoutedEventArgs e)
        {
            projectExplorerContent.Show();
        }

        void menuItem_File_SaveAs_Click(object sender, RoutedEventArgs e)
        {
            if (activeEditor != null)
            {
                SaveFileDialog saveFileDialog = new SaveFileDialog();
                saveFileDialog.DefaultExt = ".nut";
                saveFileDialog.FileName = activeEditor.Filename;
                saveFileDialog.Filter = "Squirrel Script File (*.nut)|*.nut|All Files (*.*)|*.*";
                saveFileDialog.InitialDirectory = Path.GetDirectoryName(activeEditor.Filepath);

                Nullable<bool> result = saveFileDialog.ShowDialog();
                if (result == true)
                {
                    activeEditor.SaveAs(saveFileDialog.FileName);
                    StatusText = "File Saved";
                }

                UpdateMenus();
            }
        }

        void menuItem_Help_Squirrel_LanguageReference_Click(object sender, RoutedEventArgs e)
        {
            Process.Start("http://squirrel-lang.org/doc/squirrel3.html");
        }

        void menuItem_Help_Squirrel_StandardLibraryReference_Click(object sender, RoutedEventArgs e)
        {
            Process.Start("http://squirrel-lang.org/doc/sqstdlib3.html");
        }

        void menuItem_Help_DelGEReference_Click(object sender, RoutedEventArgs e)
        {
            Process.Start(Path.GetDirectoryName(GetType().Assembly.Location) + "\\docs\\index.html");
        }

        void menuItem_Help_About_Click(object sender, RoutedEventArgs e)
        {
            Window_About aboutWindow = new Window_About();
            aboutWindow.ShowDialog();
        }

        void activeProject_ProjectItemRenamed(object sender, EventArgs e)
        {
            UpdateMenus();
        }

        void activeProject_ProjectItemRemoved(object sender, EventArgs e)
        {
            if (activeProject != null)
                activeProject.PopulateTreeView(projectExplorer);
        }

        private void CommandNewProjectCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void CommandNewFileCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (activeProject != null) e.CanExecute = true;
        }

        private void CommandOpenProjectCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void CommandOpenFileCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void CommandCloseProjectCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (activeProject != null) e.CanExecute = true;
        }

        private void CommandCloseFileCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (activeEditor != null) e.CanExecute = true;
        }

        private void CommandSaveFileCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (activeEditor != null) e.CanExecute = true;
        }

        private void CommandSaveAllCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = true;
        }

        private void CommandSaveProjectCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (activeProject != null) e.CanExecute = true;
        }

        private void CommandRunCanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = false;
            if (activeProject != null) e.CanExecute = true;
        }

        private void CommandNewProjectExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Dialog_NewProject newProjectDialog = new Dialog_NewProject();
            newProjectDialog.ProjectName = "Project";
            newProjectDialog.ProjectPath = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);
            if (newProjectDialog.ShowDialog() == true)
            {
                string safeProjectName = Project.SafeFilename(newProjectDialog.ProjectName);
                string projectPath = newProjectDialog.ProjectPath;
                if (newProjectDialog.CreateDirectory)
                {
                    projectPath += "\\" + safeProjectName;
                    if (!Directory.Exists(projectPath))
                        Directory.CreateDirectory(projectPath);
                }

                Project project = new Project(newProjectDialog.ProjectName, projectPath);

                project.AddScreen("myScreen", true);
                project.AddGameScript(project.Name, "myScreen", true);
                project.Save();

                project.PopulateTreeView(projectExplorer);

                if (activeProject != null)
                    activeProject.Close();
                activeProject = project;
                activeProject.ProjectItemRenamed += new Project.ProjectItemRenamedEventHandler(activeProject_ProjectItemRenamed);
                activeProject.ProjectItemRemoved += new Project.ProjectItemRemovedEventHandler(activeProject_ProjectItemRemoved);

                UpdateMenus();
                StatusText = "Project Created";
                Title = activeProject.Name + " - DelGE Squirrel Editor";
            }
        }

        private void CommandNewFileExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Dialog_NewFile newFileDialog = new Dialog_NewFile();
            if (newFileDialog.ShowDialog() == true)
            {
                ProjectItem item = null;

                switch (newFileDialog.SelectedFileTypeName)
                {
                    case "Class":
                        item = activeProject.AddClass(Project.SafeFilename(newFileDialog.Filename), true);
                        break;

                    case "Screen":
                        item = activeProject.AddScreen(Project.SafeFilename(newFileDialog.Filename), true);
                        break;

                    case "Script":
                        item = activeProject.AddScript(Project.SafeFilename(newFileDialog.Filename), true);
                        break;
                }

                if (item != null)
                {
                    activeProject.PopulateTreeView(projectExplorer);

                    StatusText = "File Created";

                    item.Editor.Show(dockManager);
                }
            }
        }

        private void CommandOpenProjectExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if ((activeProject != null) && (!activeProject.Close()))
                return;

            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.DefaultExt = ".prj";
            openFileDialog.FileName = "";
            openFileDialog.Filter = "Delge Squirrel Project (*.prj)|*.prj";
            openFileDialog.InitialDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            Nullable<bool> result = openFileDialog.ShowDialog();
            if (result == true)
            {
                Project project = Project.OpenProject(openFileDialog.FileName);
                if (project != null)
                {
                    activeProject = project;
                    activeProject.PopulateTreeView(projectExplorer);
                    activeProject.ProjectItemRemoved += new Project.ProjectItemRemovedEventHandler(activeProject_ProjectItemRemoved);
                    activeProject.ProjectItemRenamed += new Project.ProjectItemRenamedEventHandler(activeProject_ProjectItemRenamed);
                    UpdateMenus();
                    StatusText = "Project Opened";
                    Title = activeProject.Name + " - DelGE Squirrel Editor";
                }
            }
        }

        private void CommandOpenFileExecute(object sender, ExecutedRoutedEventArgs e)
        {
            OpenFileDialog openFileDialog = new OpenFileDialog();
            openFileDialog.DefaultExt = ".nut";
            openFileDialog.FileName = "";
            openFileDialog.Filter = "Squirrel Script File (*.nut)|*.nut|All Files (*.*)|*.*";
            openFileDialog.InitialDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location);

            Nullable<bool> result = openFileDialog.ShowDialog();
            if (result == true)
            {
                ScriptEditor editor = new ScriptEditor(openFileDialog.FileName);
                editor.Show(dockManager);
                editor.Activate();
                UpdateMenus();
                StatusText = "File Opened";
            }
        }

        private void CommandCloseProjectExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (activeProject != null)
            {
                bool cancel = !activeProject.Close();

                if (!cancel)
                {
                    activeProject = null;
                    projectExplorer.Items.Clear();

                    UpdateMenus();

                    StatusText = "Project Closed";
                    Title = "DelGE Squirrel Editor";
                }
            }
        }

        private void CommandCloseFileExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (activeEditor != null)
            {
                if (activeEditor.Close()) StatusText = "File Closed";
                UpdateMenus();
            }
        }

        private void CommandSaveFileExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (activeEditor != null) { activeEditor.Save(); StatusText = "File Saved"; }
        }

        private void CommandSaveAllExecute(object sender, ExecutedRoutedEventArgs e)
        {
            ScriptEditor.SaveAll();
            StatusText = "File(s) Saved";
        }

        private void CommandSaveProjectExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (activeProject != null) { activeProject.Save(); StatusText = "Project Saved"; }
        }

        private void CommandRunExecute(object sender, ExecutedRoutedEventArgs e)
        {
            if (scriptProcess != null && scriptProcess.HasExited == false)
            {
                MessageBox.Show("Program already running!", "DelGE Suirrel Editor", MessageBoxButton.OK, MessageBoxImage.Error);
                return;
            }

            if (activeProject != null)
            {
                activeProject.Save();

                scriptProcess = new Process();
                scriptProcess.EnableRaisingEvents = true;
                scriptProcess.StartInfo.UseShellExecute = false;
                scriptProcess.StartInfo.CreateNoWindow = false;
                scriptProcess.StartInfo.RedirectStandardOutput = true;
                scriptProcess.StartInfo.RedirectStandardError = true;
                scriptProcess.StartInfo.WorkingDirectory = Path.GetDirectoryName(activeProject.Filepath);
                scriptProcess.StartInfo.FileName = Path.GetDirectoryName(GetType().Assembly.Location) + "//bin//delge_squirrel.exe";
                scriptProcess.Exited += new EventHandler(scriptProcess_Exited);
                scriptProcess.Start();
            }
        }

        private void scriptProcess_Exited(object sender, EventArgs e)
        {
            string errorType = scriptProcess.StandardError.ReadLine();

            switch (errorType)
            {
                case "Runtime Error":
                    {
                        Error error = new Error()
                        {
                            Description = scriptProcess.StandardError.ReadLine(),
                            Source = scriptProcess.StandardError.ReadLine(),
                            FunctionName = scriptProcess.StandardError.ReadLine(),
                            LineNumber = Convert.ToInt32(scriptProcess.StandardError.ReadLine())
                        };
                        MessageBox.Show(error.Description + "\n\nError Occurred in \"" + error.Source + "\" On line \"" + error.LineNumber + "\"", "Runtime Error", MessageBoxButton.OK, MessageBoxImage.Error);
                        break;
                    }

                case "Compile Error":
                    {
                        Error error = new Error()
                        {
                            Description = scriptProcess.StandardError.ReadLine(),
                            Source = scriptProcess.StandardError.ReadLine(),
                            FunctionName = "",
                            LineNumber = Convert.ToInt32(scriptProcess.StandardError.ReadLine())
                        };
                        MessageBox.Show(error.Description + "\n\nError Occurred in \"" + error.Source + "\" On line \"" + error.LineNumber + "\"", "Compile Error", MessageBoxButton.OK, MessageBoxImage.Error);
                        break;
                    }

                default:
                    break;
            }

            Console.Write(scriptProcess.StandardOutput.ReadToEnd());
           // MessageBox.Show(scriptProcess.StandardOutput.ReadToEnd(), "Output", MessageBoxButton.OK, MessageBoxImage.Error);
        }
    }
}
